#include <stdio.h>
#include <string.h>

#include "mpp_encoder.h"
#include "rockchip/mpp_buffer.h"

#define MPP_ALIGN(x, a) (((x)+(a)-1)&~((a)-1))
#define SZ_4K 4096
#define LOGD printf
// #define LOGD
#define LOGE printf

int MppEncoder::ChangeBps(int bps)
{
	MPP_RET ret;

	ret = mpp_mpi->control(mpp_ctx, MPP_ENC_GET_CFG, cfg);
	if (ret) {
		LOGD("%p failed to get encoder cfg ret %d ", mpp_ctx, ret);
		return -1;
	}

	mpp_enc_cfg_set_s32(cfg, "rc:bps_target", bps);
	mpp_enc_cfg_set_s32(cfg, "rc:bps_max", bps);
	mpp_enc_cfg_set_s32(cfg, "rc:bps_min", bps / 2);

	ret = mpp_mpi->control(mpp_ctx, MPP_ENC_SET_CFG, cfg);
	if (ret) {
		LOGE("mpi control enc set cfg failed ret %d\n", ret);
		return -2;
	}

	return 0;
}

int MppEncoder::InitParams(MppEncoderParams& params)
{
	memcpy(&enc_params, &params, sizeof(MppEncoderParams));

	// get paramter from cmd
	if (enc_params.hor_stride == 0) {
		enc_params.hor_stride = MPP_ALIGN(enc_params.width, 16);
	}
	if (enc_params.ver_stride == 0) {
		enc_params.ver_stride = (MPP_ALIGN(enc_params.height, 16));
	}

	if (enc_params.fps_in_den == 0)
		enc_params.fps_in_den = 1;
	if (enc_params.fps_in_num == 0)
		enc_params.fps_in_num = 30;
	if (enc_params.fps_out_den == 0)
		enc_params.fps_out_den = 1;
	if (enc_params.fps_out_num == 0)
		enc_params.fps_out_num = 30;

	if (!enc_params.bps)
		enc_params.bps = enc_params.width * enc_params.height / 8 * (enc_params.fps_out_num / enc_params.fps_out_den);

	this->mdinfo_size  = (MPP_VIDEO_CodingHEVC == enc_params.type) ?
					  (MPP_ALIGN(enc_params.hor_stride, 32) >> 5) *
					  (MPP_ALIGN(enc_params.ver_stride, 32) >> 5) * 16 :
					  (MPP_ALIGN(enc_params.hor_stride, 64) >> 6) *
					  (MPP_ALIGN(enc_params.ver_stride, 16) >> 4) * 16;

	// update resource parameter
	switch (enc_params.fmt & MPP_FRAME_FMT_MASK) {
	case MPP_FMT_YUV420SP:
	case MPP_FMT_YUV420P: {
		this->frame_size = MPP_ALIGN(enc_params.hor_stride, 64) * MPP_ALIGN(enc_params.ver_stride, 64) * 3 / 2;
	} break;

	case MPP_FMT_YUV422_YUYV :
	case MPP_FMT_YUV422_YVYU :
	case MPP_FMT_YUV422_UYVY :
	case MPP_FMT_YUV422_VYUY :
	case MPP_FMT_YUV422P :
	case MPP_FMT_YUV422SP : {
		this->frame_size = MPP_ALIGN(enc_params.hor_stride, 64) * MPP_ALIGN(enc_params.ver_stride, 64) * 2;
	} break;
	case MPP_FMT_RGB444 :
	case MPP_FMT_BGR444 :
	case MPP_FMT_RGB555 :
	case MPP_FMT_BGR555 :
	case MPP_FMT_RGB565 :
	case MPP_FMT_BGR565 :
	case MPP_FMT_RGB888 :
	case MPP_FMT_BGR888 :
	case MPP_FMT_RGB101010 :
	case MPP_FMT_BGR101010 :
	case MPP_FMT_ARGB8888 :
	case MPP_FMT_ABGR8888 :
	case MPP_FMT_BGRA8888 :
	case MPP_FMT_RGBA8888 : {
		this->frame_size = MPP_ALIGN(enc_params.hor_stride, 64) * MPP_ALIGN(enc_params.ver_stride, 64);
	} break;

	default: {
		this->frame_size = MPP_ALIGN(enc_params.hor_stride, 64) * MPP_ALIGN(enc_params.ver_stride, 64) * 4;
	} break;
	}

	if (MPP_FRAME_FMT_IS_FBC(enc_params.fmt)) {
		if ((enc_params.fmt & MPP_FRAME_FBC_MASK) == MPP_FRAME_FBC_AFBC_V1)
			this->header_size = MPP_ALIGN(MPP_ALIGN(enc_params.width, 16) * MPP_ALIGN(enc_params.height, 16) / 16, SZ_4K);
		else
			this->header_size = MPP_ALIGN(enc_params.width, 16) * MPP_ALIGN(enc_params.height, 16) / 16;
	} else {
		this->header_size = 0;
	}

	return 0;
}

int MppEncoder::SetupEncCfg()
{
	MPP_RET ret;

	ret = mpp_enc_cfg_init(&cfg);
	if (ret) {
		LOGE("mpp_enc_cfg_init failed ret %d\n", ret);
		return -1;
	}

	/* setup default parameter */
	if (enc_params.fps_in_den == 0)
		enc_params.fps_in_den = 1;
	if (enc_params.fps_in_num == 0)
		enc_params.fps_in_num = 30;
	if (enc_params.fps_out_den == 0)
		enc_params.fps_out_den = 1;
	if (enc_params.fps_out_num == 0)
		enc_params.fps_out_num = 30;

	if (!enc_params.bps)
		enc_params.bps = enc_params.width * enc_params.height / 8 * (enc_params.fps_out_num / enc_params.fps_out_den);

	LOGD("bps = %d, width = %d, height = %d, ", enc_params.bps, enc_params.width, enc_params.height);

	mpp_enc_cfg_set_s32(cfg, "prep:width", enc_params.width);
	mpp_enc_cfg_set_s32(cfg, "prep:height", enc_params.height);
	mpp_enc_cfg_set_s32(cfg, "prep:hor_stride", enc_params.hor_stride);
	mpp_enc_cfg_set_s32(cfg, "prep:ver_stride", enc_params.ver_stride);
	mpp_enc_cfg_set_s32(cfg, "prep:format", enc_params.fmt);

	enc_params.rc_mode = MPP_ENC_RC_MODE_AVBR;
	mpp_enc_cfg_set_s32(cfg, "rc:mode", enc_params.rc_mode);

	/* fix input / output frame rate */
	mpp_enc_cfg_set_s32(cfg, "rc:fps_in_flex", enc_params.fps_in_flex);
	mpp_enc_cfg_set_s32(cfg, "rc:fps_in_num", enc_params.fps_in_num);
	mpp_enc_cfg_set_s32(cfg, "rc:fps_in_denorm", enc_params.fps_in_den);
	mpp_enc_cfg_set_s32(cfg, "rc:fps_out_flex", enc_params.fps_out_flex);
	mpp_enc_cfg_set_s32(cfg, "rc:fps_out_num", enc_params.fps_out_num);
	mpp_enc_cfg_set_s32(cfg, "rc:fps_out_denorm", enc_params.fps_out_den);
	mpp_enc_cfg_set_s32(cfg, "rc:gop", enc_params.gop_len ? enc_params.gop_len : enc_params.fps_out_num * 2);

	/* drop frame or not when bitrate overflow */
	mpp_enc_cfg_set_u32(cfg, "rc:drop_mode", MPP_ENC_RC_DROP_FRM_DISABLED);
	mpp_enc_cfg_set_u32(cfg, "rc:drop_thd", 20);        /* 20% of max bps */
	mpp_enc_cfg_set_u32(cfg, "rc:drop_gap", 1);         /* Do not continuous drop frame */

	/* setup bitrate for different rc_mode */
	mpp_enc_cfg_set_s32(cfg, "rc:bps_target", enc_params.bps);
	switch (enc_params.rc_mode) {
	case MPP_ENC_RC_MODE_FIXQP : {
		/* do not setup bitrate on FIXQP mode */
	} break;
	case MPP_ENC_RC_MODE_CBR : {
		/* CBR mode has narrow bound */
		mpp_enc_cfg_set_s32(cfg, "rc:bps_max", enc_params.bps_max ? enc_params.bps_max : enc_params.bps * 17 / 16);
		mpp_enc_cfg_set_s32(cfg, "rc:bps_min", enc_params.bps_min ? enc_params.bps_min : enc_params.bps * 15 / 16);
	} break;
	case MPP_ENC_RC_MODE_VBR :
	case MPP_ENC_RC_MODE_AVBR : {
		/* VBR mode has wide bound */
		mpp_enc_cfg_set_s32(cfg, "rc:bps_max", enc_params.bps_max ? enc_params.bps_max : enc_params.bps * 17 / 16);
		mpp_enc_cfg_set_s32(cfg, "rc:bps_min", enc_params.bps_min ? enc_params.bps_min : enc_params.bps * 1 / 16);
	} break;
	default : {
		/* default use CBR mode */
		mpp_enc_cfg_set_s32(cfg, "rc:bps_max", enc_params.bps_max ? enc_params.bps_max : enc_params.bps * 17 / 16);
		mpp_enc_cfg_set_s32(cfg, "rc:bps_min", enc_params.bps_min ? enc_params.bps_min : enc_params.bps * 15 / 16);
	} break;
	}

	/* setup qp for different codec and rc_mode */
	switch (enc_params.type) {
	case MPP_VIDEO_CodingAVC :
	case MPP_VIDEO_CodingHEVC : {
		switch (enc_params.rc_mode) {
		case MPP_ENC_RC_MODE_FIXQP : {
			RK_S32 fix_qp = enc_params.qp_init;

			mpp_enc_cfg_set_s32(cfg, "rc:qp_init", fix_qp);
			mpp_enc_cfg_set_s32(cfg, "rc:qp_max", fix_qp);
			mpp_enc_cfg_set_s32(cfg, "rc:qp_min", fix_qp);
			mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", fix_qp);
			mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", fix_qp);
			mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 0);
		} break;
		case MPP_ENC_RC_MODE_CBR :
		case MPP_ENC_RC_MODE_VBR :
		case MPP_ENC_RC_MODE_AVBR : {
			mpp_enc_cfg_set_s32(cfg, "rc:qp_init", -1);
			mpp_enc_cfg_set_s32(cfg, "rc:qp_max", 51);
			mpp_enc_cfg_set_s32(cfg, "rc:qp_min", 10);
			mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", 51);
			mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", 10);
			mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 2);
		} break;
		default : {
			LOGE("unsupport encoder rc mode %d\n", enc_params.rc_mode);
		} break;
		}
	} break;
	case MPP_VIDEO_CodingVP8 : {
		/* vp8 only setup base qp range */
		mpp_enc_cfg_set_s32(cfg, "rc:qp_init", 40);
		mpp_enc_cfg_set_s32(cfg, "rc:qp_max",  127);
		mpp_enc_cfg_set_s32(cfg, "rc:qp_min",  0);
		mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", 127);
		mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", 0);
		mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 6);
	} break;
	case MPP_VIDEO_CodingMJPEG : {
		/* jpeg use special codec config to control qtable */
		mpp_enc_cfg_set_s32(cfg, "jpeg:q_factor", 80);
		mpp_enc_cfg_set_s32(cfg, "jpeg:qf_max", 99);
		mpp_enc_cfg_set_s32(cfg, "jpeg:qf_min", 1);
	} break;
	default : {
	} break;
	}

	/* setup codec  */
	mpp_enc_cfg_set_s32(cfg, "codec:type", enc_params.type);
	switch (enc_params.type) {
	case MPP_VIDEO_CodingAVC : {
		RK_U32 constraint_set = enc_params.constraint_set;

		/*
		 * H.264 profile_idc parameter
		 * 66  - Baseline profile
		 * 77  - Main profile
		 * 100 - High profile
		 */
		mpp_enc_cfg_set_s32(cfg, "h264:profile", 100);
		/*
		 * H.264 level_idc parameter
		 * 10 / 11 / 12 / 13    - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps
		 * 20 / 21 / 22         - cif@30fps / half-D1@@25fps / D1@12.5fps
		 * 30 / 31 / 32         - D1@25fps / 720p@30fps / 720p@60fps
		 * 40 / 41 / 42         - 1080p@30fps / 1080p@30fps / 1080p@60fps
		 * 50 / 51 / 52         - 4K@30fps
		 */
		mpp_enc_cfg_set_s32(cfg, "h264:level", 40);
		mpp_enc_cfg_set_s32(cfg, "h264:cabac_en", 1);
		mpp_enc_cfg_set_s32(cfg, "h264:cabac_idc", 0);
		mpp_enc_cfg_set_s32(cfg, "h264:trans8x8", 1);

		if (constraint_set & 0x3f0000)
			mpp_enc_cfg_set_s32(cfg, "h264:constraint_set", constraint_set);
	} break;
	case MPP_VIDEO_CodingHEVC :
	case MPP_VIDEO_CodingMJPEG :
	case MPP_VIDEO_CodingVP8 : {
	} break;
	default : {
		LOGE("unsupport encoder coding type %d\n", enc_params.type);
	} break;
	}

	if (enc_params.split_mode) {
		LOGD("%p split mode %d arg %d out %d\n", mpp_ctx,
				  enc_params.split_mode, enc_params.split_arg, enc_params.split_out);
		mpp_enc_cfg_set_s32(cfg, "split:mode", enc_params.split_mode);
		mpp_enc_cfg_set_s32(cfg, "split:arg", enc_params.split_arg);
		mpp_enc_cfg_set_s32(cfg, "split:out", enc_params.split_out);
	}

	mpp_enc_cfg_set_s32(cfg, "prep:mirroring", enc_params.mirroring);
	mpp_enc_cfg_set_s32(cfg, "prep:rotation", enc_params.rotation);
	mpp_enc_cfg_set_s32(cfg, "prep:flip", enc_params.flip);

	ret = mpp_mpi->control(mpp_ctx, MPP_ENC_SET_CFG, cfg);
	if (ret) {
		LOGE("mpi control enc set cfg failed ret %d\n", ret);
		goto RET;
	}

#if 0
	/* optional */
	{
		ret = mpp_mpi->control(mpp_ctx, MPP_ENC_SET_SEI_CFG, &enc_params.sei_mode);
		if (ret) {
			LOGE("mpi control enc set sei cfg failed ret %d\n", ret);
			goto RET;
		}
	}
#endif

	if (enc_params.type == MPP_VIDEO_CodingAVC || enc_params.type == MPP_VIDEO_CodingHEVC) {
		enc_params.header_mode = MPP_ENC_HEADER_MODE_EACH_IDR;
		ret = mpp_mpi->control(mpp_ctx, MPP_ENC_SET_HEADER_MODE, &enc_params.header_mode);
		if (ret) {
			LOGE("mpi control enc set header mode failed ret %d\n", ret);
			goto RET;
		}
	}

#if 0
	RK_U32 gop_mode = enc_params.gop_mode;
	if (gop_mode) {
		MppEncRefCfg ref;

		mpp_enc_ref_cfg_init(&ref);

		if (enc_params.gop_mode < 4)
			mpi_enc_gen_ref_cfg(ref, gop_mode);
		else
			mpi_enc_gen_smart_gop_ref_cfg(ref, enc_params.gop_len, enc_params.vi_len);

		ret = mpp_mpi->control(mpp_ctx, MPP_ENC_SET_REF_CFG, ref);
		if (ret) {
			LOGE("mpi control enc set ref cfg failed ret %d\n", ret);
			goto RET;
		}
		mpp_enc_ref_cfg_deinit(&ref);
	}

	if (enc_params.roi_enable) {
		mpp_enc_roi_init(&enc_params.roi_mpp_ctx, enc_params.width, enc_params.height, enc_params.type, 4);
		mpp_assert(enc_params.roi_mpp_ctx);
	}
#endif

RET:
	return ret;
}

MppEncoder::MppEncoder() {
	memset(&enc_params, 0, sizeof(MppEncoderParams));
	this->mpp_ctx = NULL;
	this->mpp_mpi = NULL;
	memset(&osd_data, 0, sizeof(MppEncOSDData));
}

MppEncoder::~MppEncoder() {
	if (this->mpp_ctx) {
		mpp_destroy(this->mpp_ctx);
		this->mpp_ctx = NULL;
	}

	if (this->cfg) {
		mpp_enc_cfg_deinit(this->cfg);
		this->cfg = NULL;
	}

	if (this->frm_buf) {
		mpp_buffer_put(this->frm_buf);
		this->frm_buf = NULL;
	}

	if (this->pkt_buf) {
		mpp_buffer_put(this->pkt_buf);
		this->pkt_buf = NULL;
	}

	if (this->md_info) {
		mpp_buffer_put(this->md_info);
		this->md_info = NULL;
	}

	if (this->osd_data.buf) {
		mpp_buffer_put(this->osd_data.buf);
		this->osd_data.buf = NULL;
	}

	if (this->buf_grp) {
		mpp_buffer_group_put(this->buf_grp);
		this->buf_grp = NULL;
	}
}

int MppEncoder::Init(MppEncoderParams& params, void* userdata) {
	int ret;
	MppPollType timeout = MPP_POLL_BLOCK;

	this->userdata = userdata;

	this->InitParams(params);

	ret = mpp_buffer_group_get_internal(&this->buf_grp, MPP_BUFFER_TYPE_DRM);
	if (ret) {
		LOGD("failed to get mpp buffer group ret %d\n", ret);
		goto MPP_TEST_OUT;
	}

	ret = mpp_buffer_get(this->buf_grp, &this->pkt_buf, this->frame_size);
	if (ret) {
		LOGD("failed to get buffer for output packet ret %d\n", ret);
		goto MPP_TEST_OUT;
	}

	ret = mpp_buffer_get(this->buf_grp, &this->md_info, this->mdinfo_size);
	if (ret) {
		LOGD("failed to get buffer for motion info output packet ret %d\n", ret);
		goto MPP_TEST_OUT;
	}

	// encoder demo
	ret = mpp_create(&this->mpp_ctx, &this->mpp_mpi);
	if (ret) {
		LOGE("mpp_create failed ret %d\n", ret);
		goto MPP_TEST_OUT;
	}

	LOGD("%p encoder test start w %d h %d type %d\n",
			  this->mpp_ctx, enc_params.width, enc_params.height, enc_params.type);

	ret = mpp_mpi->control(mpp_ctx, MPP_SET_OUTPUT_TIMEOUT, &timeout);
	if (MPP_OK != ret) {
		LOGE("mpi control set output timeout %d ret %d\n", timeout, ret);
		goto MPP_TEST_OUT;
	}

	ret = mpp_init(mpp_ctx, MPP_CTX_ENC, enc_params.type);
	if (ret) {
		LOGE("mpp_init failed ret %d\n", ret);
		goto MPP_TEST_OUT;
	}

	this->SetupEncCfg();

MPP_TEST_OUT:
	if (ret) {
		if (this->mpp_ctx) {
			mpp_destroy(this->mpp_ctx);
			this->mpp_ctx = NULL;
		}

		if (this->cfg) {
			mpp_enc_cfg_deinit(this->cfg);
			this->cfg = NULL;
		}

		if (this->frm_buf) {
			mpp_buffer_put(this->frm_buf);
			this->frm_buf = NULL;
		}

		if (this->pkt_buf) {
			mpp_buffer_put(this->pkt_buf);
			this->pkt_buf = NULL;
		}

		if (this->md_info) {
			mpp_buffer_put(this->md_info);
			this->md_info = NULL;
		}

		if (this->osd_data.buf) {
			mpp_buffer_put(this->osd_data.buf);
			this->osd_data.buf = NULL;
		}

		if (this->buf_grp) {
			mpp_buffer_group_put(this->buf_grp);
			this->buf_grp = NULL;
		}

		// if (this->roi_ctx) {
		//     mpp_enc_roi_deinit(this->roi_ctx);
		//     this->roi_ctx = NULL;
		// }
	}

	return ret;
}

int MppEncoder::SetCallback(MppEncoderFrameCallback callback) {
	this->callback = callback;
	return 0;
}

int MppEncoder::GetHeader(char* enc_buf, int max_size) {
	int ret;
	void* out_ptr = enc_buf;
	size_t out_len = 0;

	if (enc_params.type == MPP_VIDEO_CodingAVC || enc_params.type == MPP_VIDEO_CodingHEVC) {
		MppPacket packet = NULL;

		/*
		 * Can use packet with normal malloc buffer as input not pkt_buf.
		 * Please refer to vpu_api_legacy.cpp for normal buffer case.
		 * Using pkt_buf buffer here is just for simplifing demo.
		 */
		mpp_packet_init_with_buffer(&packet, this->pkt_buf);
		/* NOTE: It is important to clear output packet length!! */
		mpp_packet_set_length(packet, 0);

		ret = mpp_mpi->control(mpp_ctx, MPP_ENC_GET_HDR_SYNC, packet);
		if (ret) {
			LOGD("mpi control enc get extra info failed\n");
			return -1;
		} else {
			/* get and write sps/pps for H.264 */

			void *ptr   = mpp_packet_get_pos(packet);
			size_t len  = mpp_packet_get_length(packet);

			memcpy(out_ptr, ptr, len);
			out_ptr = (char*)(out_ptr) + len;
			out_len += len;
		}

		mpp_packet_deinit(&packet);
	}
	return out_len;
}

int MppEncoder::Encode(void* mpp_buf, char* enc_buf, int max_size) {
	MPP_RET ret;
	void* out_ptr = enc_buf;
	size_t out_len = 0;

	MppMeta meta = NULL;
	MppFrame frame = NULL;
	MppPacket packet = NULL;
	// void *buf = mpp_buffer_get_ptr(this->frm_buf);
	// RK_S32 cam_frm_idx = -1;
	// MppBuffer cam_buf = NULL;
	RK_U32 eoi = 1;
	RK_U32 frm_eos = 0;

	ret = mpp_frame_init(&frame);
	if (ret) {
		LOGD("mpp_frame_init failed\n");
		return -1;
	}

	mpp_frame_set_width(frame, enc_params.width);
	mpp_frame_set_height(frame, enc_params.height);
	mpp_frame_set_hor_stride(frame, enc_params.hor_stride);
	mpp_frame_set_ver_stride(frame, enc_params.ver_stride);
	mpp_frame_set_fmt(frame, enc_params.fmt);
	mpp_frame_set_eos(frame, frm_eos);
	mpp_frame_set_buffer(frame, mpp_buf);

	meta = mpp_frame_get_meta(frame);
	mpp_packet_init_with_buffer(&packet, pkt_buf);
	/* NOTE: It is important to clear output packet length!! */
	mpp_packet_set_length(packet, 0);
	mpp_meta_set_packet(meta, KEY_OUTPUT_PACKET, packet);
	mpp_meta_set_buffer(meta, KEY_MOTION_INFO, this->md_info);

	/*
	 * NOTE: in non-block mode the frame can be resent.
	 * The default input timeout mode is block.
	 *
	 * User should release the input frame to meet the requirements of
	 * resource creator must be the resource destroyer.
	 */
	ret = mpp_mpi->encode_put_frame(mpp_ctx, frame);
	mpp_frame_deinit(&frame);

	if (ret) {
		LOGD("chn %d encode put frame failed\n", chn);
		return -1;
	}

	do {
		ret = mpp_mpi->encode_get_packet(mpp_ctx, &packet);
		if (ret) {
			LOGD("chn %d encode get packet failed\n", chn);
			return -1;
		}

		// mpp_assert(packet);
		if (packet) {
			// write packet to file here
			void *ptr   = mpp_packet_get_pos(packet);
			size_t len  = mpp_packet_get_length(packet);
			char log_buf[256];
			RK_S32 log_size = sizeof(log_buf) - 1;
			RK_S32 log_len = 0;

			/* set encode result */
			if (this->callback != nullptr) {
				this->callback(this->userdata, (const char*)ptr, len);
			}
			if (enc_buf != nullptr && max_size > 0) {
				if (out_len + log_len < max_size) {
					memcpy(out_ptr, ptr, len);
					out_len += len;
					out_ptr = (char*)out_ptr + len;
				} else {
					LOGE("error enc_buf no enought");
				}
			}
			/* for low delay partition encoding */
			if (mpp_packet_is_partition(packet)) {
				eoi = mpp_packet_is_eoi(packet);
			}
			mpp_packet_deinit(&packet);
		}
	} while (!eoi);

	return out_len;
}

int MppEncoder::Reset() {
	if (mpp_mpi != NULL) {
		mpp_mpi->reset(mpp_ctx);
	}
	return 0;
}

size_t MppEncoder::GetFrameSize() {
	return this->frame_size;
}

void* MppEncoder::ImportBuffer(int index, size_t size, int fd, int type) {
	MppBuffer buf;
	MppBufferInfo info;
	memset(&info, 0, sizeof(MppBufferInfo));
	info.type = (MppBufferType)type; // MPP_BUFFER_TYPE_EXT_DMA
	info.fd =  fd;
	info.size = size;
	info.index = index;
	mpp_buffer_import(&buf, &info);
	return buf;
}

void* MppEncoder::GetInputFrameBuffer() {
	int ret;
	if (this->frm_buf == nullptr) {
		ret = mpp_buffer_get(this->buf_grp, &this->frm_buf, this->frame_size);
		if (ret) {
			LOGD("failed to get buffer for input frame ret %d\n", ret);
			return NULL;
		}
	}
	return this->frm_buf;
}

int MppEncoder::GetInputFrameBufferFd(void* mpp_buffer) {
	return mpp_buffer_get_fd(mpp_buffer);
}

void* MppEncoder::GetInputFrameBufferAddr(void* mpp_buffer) {
	return mpp_buffer_get_ptr(mpp_buffer);
}